/* 
 * Copyright (C) Dirk Husemann, Computer Science Department IV, 
 * 		 University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 * 
 * This code is derived from software contributed to Berkeley by
 * Dirk Husemann and the Computer Science Department (IV) of
 * the University of Erlangen-Nuremberg, Germany.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)pk_llcsubr.c	8.1 (Berkeley) 6/10/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_llc.h>
#include <net/if_types.h>
#include <net/route.h>

#include <netccitt/dll.h>
#include <netccitt/x25.h>
#include <netccitt/pk.h>
#include <netccitt/pk_var.h>
#include <netccitt/llc_var.h>


/*
 * Routing support for X.25
 *
 * We distinguish between two cases:
 * RTF_HOST:
 * 	rt_key(rt)	X.25 address of host
 *	rt_gateway	SNPA (MAC+DLSAP) address of host
 *	rt_llinfo	pkcb for rt_key(rt)
 *
 * RTF_GATEWAY
 *	rt_key(rt)	X.25 address of host or suitably masked network
 *	rt_gateway	X.25 address of next X.25 gateway (switch)
 *	rt_llinfo	rtentry for rt_gateway address
 *			ought to be of type RTF_HOST
 *
 *
 * Mapping of X.121 to pkcbs:
 *
 * HDLC uses the DTE-DCE model of X.25, therefore we need a many-to-one
 * relationship, i.e.:
 *	
 * 	{X.121_a, X.121_b, X.121_c, ..., X.121_i} -> pkcb_0
 *
 * LLC2 utilizes the DTE-DTE model of X.25, resulting effectively in a
 * one-to-one relationship, i.e.:
 *
 *	{X.121_j} 	->	pkcb_1a
 *	{X.121_k}	->	pkcb_1b
 *	...
 *	{X.121_q}	->	pkcb_1q
 * 
 * It might make sense to allow a many-to-one relation for LLC2 also,
 * 
 *	{X.121_r, X.121_s, X.121_t, X.121_u} -> pkcb_2a
 *
 * This would make addresses X.121_[r-u] essentially aliases of one
 * address ({X.121_[r-u]} would constitute a representative set).
 *
 * Each one-to-one relation must obviously be entered individually with
 * a route add command, whereas a many-to-one relationship can be 
 * either entered individually or generated by using a netmask.
 * 
 * To facilitate dealings the many-to-one case for LLC2 can only be
 * established via a netmask.
 *
 */

#define XTRACTPKP(rt)	((rt)->rt_flags & RTF_GATEWAY ? \
			 ((rt)->rt_llinfo ? \
			  (struct pkcb *) ((struct rtentry *)((rt)->rt_llinfo))->rt_llinfo : \
			  (struct pkcb *) NULL) : \
			 (struct pkcb *)((rt)->rt_llinfo))

#define equal(a1, a2) (bcmp((caddr_t)(a1), \
			       (caddr_t)(a2), \
			       (a1)->sa_len) == 0)
#define XIFA(rt) ((struct x25_ifaddr *)((rt)->rt_ifa))
#define SA(s) ((struct sockaddr *)s)

int
cons_rtrequest(int cmd, struct rtentry *rt, struct sockaddr *dst)
{
	register struct pkcb *pkp;
	register int i;
	register char one_to_one;
	struct pkcb *pk_newlink();
	struct rtentry *npaidb_enter();

	pkp = XTRACTPKP(rt);

	switch(cmd) {
	case RTM_RESOLVE:
	case RTM_ADD:
		if (pkp) 
			return(EEXIST);

		if (rt->rt_flags & RTF_GATEWAY) {
			if (rt->rt_llinfo)
				RTFREE((struct rtentry *)rt->rt_llinfo);
			rt->rt_llinfo = (caddr_t) rtalloc1(rt->rt_gateway, 1);
			return(0);
		}
		/*
		 * Assumptions:	(1) ifnet structure is filled in
		 *		(2) at least the pkcb created via 
		 *		    x25config (ifconfig?) has been 
		 *		    set up already.
		 *		(3) HDLC interfaces have an if_type of 
		 *		    IFT_X25{,DDN}, LLC2 interfaces 
		 *		    anything else (any better way to 
		 *		    do this?)
		 *
		 */
		if (!rt->rt_ifa)
			return (ENETDOWN);
	
		/*	
		 * We differentiate between dealing with a many-to-one
		 * (HDLC: DTE-DCE) and a one-to-one (LLC2: DTE-DTE) 
		 * relationship (by looking at the if type).
		 *
		 * Only in case of the many-to-one relationship (HDLC)
		 * we set the ia->ia_pkcb pointer to the pkcb allocated
		 * via pk_newlink() as we will use just that one pkcb for
		 * future route additions (the rtentry->rt_llinfo pointer
		 * points to the pkcb allocated for that route).
		 *
		 * In case of the one-to-one relationship (LLC2) we 
		 * create a new pkcb (via pk_newlink()) for each new rtentry.
		 * 
		 * NOTE: Only in case of HDLC does ia->ia_pkcb point
		 * to a pkcb, in the LLC2 case it doesn't (as we don't 
		 * need it here)!
		 */
		one_to_one = ISISO8802(rt->rt_ifp);

		if (!(pkp = XIFA(rt)->ia_pkcb) && !one_to_one) 
			XIFA(rt)->ia_pkcb = pkp = 
				pk_newlink(XIFA(rt), (caddr_t) 0);
		else if (one_to_one && 
			 !equal(rt->rt_gateway, rt->rt_ifa->ifa_addr)) {
			pkp = pk_newlink(XIFA(rt), (caddr_t) 0);
			/*
			 * We also need another route entry for mapping
			 * MAC+LSAP->X.25 address
			 */
			pkp->pk_llrt = npaidb_enter(rt->rt_gateway, rt_key(rt), rt, 0);
		}
		if (pkp) {
			if (!pkp->pk_rt)
				pkp->pk_rt = rt;
			pkp->pk_refcount++;
		}
		rt->rt_llinfo = (caddr_t) pkp;

		return(0);

	case RTM_DELETE:
	{
		/*
		 * The pkp might be empty if we are dealing
		 * with an interface route entry for LLC2, in this 
		 * case we don't need to do anything ...
		 */
		if (pkp) {
			if ( rt->rt_flags & RTF_GATEWAY ) {
				if (rt->rt_llinfo)
					RTFREE((struct rtentry *)rt->rt_llinfo);
				return(0);
			}
			
			if (pkp->pk_llrt)
				npaidb_destroy(pkp->pk_llrt);

			pk_dellink (pkp);
			
			return(0);
		}
	}
	}
}

/*
 * Network Protocol Addressing Information DataBase (npaidb) 
 * 
 * To speed up locating the entity dealing with an LLC packet use is made 
 * of a routing tree. This npaidb routing tree is handled 
 * by the normal rn_*() routines just like (almost) any other routing tree. 
 * 
 * The mapping being done by the npaidb_*() routines is as follows: 
 * 
 *     Key:       MAC,LSAP (enhancing struct sockaddr_dl) 
 *     Gateway:   sockaddr_x25 (i.e. X.25 address - X.121 or NSAP) 
 *     Llinfo:    npaidbentry { 
 *                         struct llc_linkcb *npaidb_linkp; 
 *                         struct rtentry *npaidb_rt; 
 *                } 
 * 
 * Using the npaidbentry provided by llinfo we can then access 
 * 
 *       o the pkcb by using (struct pkcb *) (npaidb_rt->rt_llinfo)
 *       o the linkcb via npaidb_linkp 
 * 
 * The following functions are provided 
 * 
 *       o npaidb_enter(struct sockaddr_dl *sdl, struct sockaddr_x25 *sx25, 
 *                      struct struct llc_linkcb *link, struct rtentry *rt) 
 * 
 *       o npaidb_enrich(short type, caddr_t info) 
 * 
 */

struct sockaddr_dl npdl_netmask = {
 sizeof(struct sockaddr_dl),					/* _len */
 0,								/* _family */
 0,								/* _index */
 0,								/* _type */
 -1,								/* _nlen */
 -1,								/* _alen */
 -1,								/* _slen */
 { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},		/* _data */
}; 
struct sockaddr npdl_dummy;

int npdl_datasize = sizeof(struct sockaddr_dl)-
		((int)((caddr_t)&((struct sockaddr_dl *)0)->sdl_data[0]));

struct rtentry *
npaidb_enter(struct sockaddr_dl *key, struct sockaddr *value,
	     struct rtentry *rt, struct llc_linkcb *link)
{
	struct rtentry *nprt; register int i;

	USES_AF_LINK_RTS;

	if ((nprt = rtalloc1(SA(key), 0)) == 0) {
		register u_int size = sizeof(struct npaidbentry);
		register u_char saploc = LLSAPLOC(key, rt->rt_ifp);

		/* 
		 * set up netmask: LLC2 packets have the lowest bit set in
		 * response packets (e.g. 0x7e for command packets, 0x7f for
		 * response packets), to facilitate the lookup we use a netmask
		 * of 11111110 for the SAP position. The remaining positions 
		 * are zeroed out.
		 */
		npdl_netmask.sdl_data[saploc] = NPDL_SAPNETMASK;
		bzero((caddr_t)&npdl_netmask.sdl_data[saploc+1], 
		      npdl_datasize-saploc-1);

		if (value == 0)
			value = &npdl_dummy;

		/* now enter it */
		rtrequest(RTM_ADD, SA(key), SA(value),
			SA(&npdl_netmask), 0, &nprt);

		/* and reset npdl_netmask */
		for (i = saploc; i < npdl_datasize; i++)
			npdl_netmask.sdl_data[i] = -1;

		nprt->rt_llinfo = malloc(size , M_PCB, M_WAITOK);
		if (nprt->rt_llinfo) {
			bzero (nprt->rt_llinfo, size);
			((struct npaidbentry *) (nprt->rt_llinfo))->np_rt = rt;
		}
	} else nprt->rt_refcnt--;
	return nprt;
}

struct rtentry *
npaidb_enrich(short type, caddr_t info, struct sockaddr_dl *sdl)
{
	struct rtentry *rt;

	USES_AF_LINK_RTS;

	if (rt = rtalloc1((struct sockaddr *)sdl, 0)) {
		rt->rt_refcnt--;
		switch (type) {
		case NPAIDB_LINK:
			((struct npaidbentry *)(rt->rt_llinfo))->np_link = 
				(struct llc_linkcb *) info;
			break;
		}
		return rt;
	}		

	return ((struct rtentry *) 0);

}

npaidb_destroy(struct rtentry *rt)
{
	USES_AF_LINK_RTS;

	if (rt->rt_llinfo) 
		free((caddr_t) rt->rt_llinfo, M_PCB);
	return(rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), 
			 0, 0));
}


#ifdef LLC
/*
 * Glue between X.25 and LLC2
 */
int
x25_llcglue(int prc, struct sockaddr *addr)
{
	register struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)addr;
	register struct x25_ifaddr *x25ifa;
	struct dll_ctlinfo ctlinfo;
	
	if((x25ifa = (struct x25_ifaddr *)ifa_ifwithaddr(addr)) == 0)
		return 0;

	ctlinfo.dlcti_cfg  =
	    (struct dllconfig *)(((struct sockaddr_x25 *)(&x25ifa->ia_xc))+1);
	ctlinfo.dlcti_lsap = LLC_X25_LSAP;

	return ((int)llc_ctlinput(prc, addr, (caddr_t)&ctlinfo));
}
#endif /* LLC */
